package org.belkodevelop.announcement.dao;

import java.util.List;
import org.belkodevelop.announcement.domain.Announcement;
import org.belkodevelop.announcement.domain.OperationEnum;
import org.belkodevelop.geocoding.adress.domen.CompositeAdress;
import org.belkodevelop.realty.factory.RealtyEnum;
import org.hibernate.Query;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Restrictions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

@Repository
public class AnnouncementDAOImpl implements AnnouncementDAO {

    private static final int MAX_FIND_RESULTS = 5000;

    @Autowired
    private SessionFactory sessionFactory;
    
    @Override
    public Announcement getAnnouncement(long id) {
        return (Announcement) sessionFactory.getCurrentSession().get(Announcement.class, id);
    }

    @Override
    public void saveAnnouncement(Announcement announcement) {
        sessionFactory.getCurrentSession().save(announcement);
    }

    @Override
    public boolean removeAnnouncement(long id) {
        Query query = sessionFactory.getCurrentSession().
                createQuery("delete from Announcement where id = :id");
        query.setParameter("id", id);
        return query.executeUpdate() > 0;
    }

    @Override
    public List<Announcement> getAnnouncementsByUser(long id) {
        return sessionFactory.getCurrentSession().createCriteria(Announcement.class).add(Restrictions.eq("owner.id", id)).list();
    }

    @Override
    public List<Announcement> find(CompositeAdress compositeAdress, RealtyEnum type, Double priceFrom, Double priceTo, OperationEnum operation) {
        
        Query query = sessionFactory.getCurrentSession().
                createQuery("from Announcement a where "
                + "(:type is null OR a.realty.class=:type) AND"
                + "(:operation is null OR a.operation=:operation) AND"
                + "(:priceFrom is null OR a.price >= :priceFrom ) AND (:priceTo is null OR a.price <= :priceTo) AND"
                + "(:country is null OR a.realty.compositeAdress.country=:country) AND"
                + "(:administrativeAreaLevel1 is null OR a.realty.compositeAdress.administrativeAreaLevel1=:administrativeAreaLevel1) AND "
                + "(:administrativeAreaLevel2 is null OR a.realty.compositeAdress.administrativeAreaLevel2=:administrativeAreaLevel2) AND "
                + "(:administrativeAreaLevel3 is null OR a.realty.compositeAdress.administrativeAreaLevel3=:administrativeAreaLevel3) AND "
                + "(:locality is null OR a.realty.compositeAdress.locality=:locality) AND"
                + "(:sublocality is null OR a.realty.compositeAdress.sublocality=:sublocality) AND"
                + "(:route is null OR a.realty.compositeAdress.route=:route) AND"
                + "(:streetAddress is null OR a.realty.compositeAdress.streetAddress=:streetAddress) AND"
                + "(:streetNumber is null OR a.realty.compositeAdress.streetNumber=:streetNumber) AND"
                + "(:floor is null OR a.realty.compositeAdress.floor=:floor) AND"
                + "(:room is null OR a.realty.compositeAdress.room=:room)");
        
        query.setParameter("type", type == null ? null : type.toString());
        query.setParameter("priceFrom", priceFrom);
        query.setParameter("priceTo", priceTo);
        query.setParameter("operation", operation.toString());

        query.setProperties(compositeAdress);
        query.setCacheable(true);
        query.setMaxResults(MAX_FIND_RESULTS);

        return (List<Announcement>)query.list();
    }

    @Override
    public Float getMaxPriceForRent() {
        Query query = sessionFactory.getCurrentSession().
                createQuery("select max(a.price) from Announcement a where a.operation = 'RENT'");
        query.setCacheable(true);
        return (Float)query.uniqueResult();
    }

    @Override
    public Float getMaxPriceForSale() {
        Query query = sessionFactory.getCurrentSession().
                createQuery("select max(a.price) from Announcement a where a.operation = 'SALE'");
        query.setCacheable(true);
        return (Float)query.uniqueResult();
    }

    @Override
    public void incrementRatingById(long id) {
        Query query = sessionFactory.getCurrentSession().createQuery("update Announcement a set a.rating=a.rating + 1 where id = :id");
        query.setParameter("id", id);
        query.executeUpdate();
    }

    @Override
    public List<Announcement> getPremiunAnnouncements() {
        Query query = sessionFactory.getCurrentSession().
                createQuery("from Announcement a where a.premium = true");

        return (List<Announcement>)query.list();
    }
}
